package cn.javadoc.db;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

import javax.sql.DataSource;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanExpressionException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;

import cn.javadoc.db.impl.AbstractImpl;
import cn.javadoc.db.impl.MergeImpl;
import cn.javadoc.db.impl.SaveImpl;
import cn.javadoc.db.impl.SpExec;
import cn.javadoc.db.impl.SqlExec;
import cn.javadoc.db.impl.UpdateImpl;

@SuppressWarnings("unused")
public class DAOImpl implements MethodInterceptor, FactoryBean<Object>,
		InitializingBean {

	private Class<?> dao;

	private static final long serialVersionUID = 1L;

	private HashMap<Method, AbstractImpl> sqlCache = new HashMap<Method, AbstractImpl>();

	private DataSource dataSource;

	// 初始化检查数据模型， 字段匹配区分大小写
	private boolean check = true;
	// 扫描的包路径，默认
	// merge,update,save名字
	private String merge = "merge", update = "update", save = "save",
			fieldMatchReg = "[-_]";

	@Override
	public void afterPropertiesSet() throws Exception {
		if (dao == null) {
			throw new BeansException(
					"dao interface or dao basePackage not set!") {
				private static final long serialVersionUID = 1L;
			};
		}
		if (!dao.isInterface()) {
			throw new BeanExpressionException("dao class not interface:"
					+ dao.getClass().getName());
		}
		enhancer.setSuperclass(dao);
		enhancer.setCallback(this);
		Connection conn = null;
		if (check)
			conn = dataSource.getConnection();
		Method ms[] = dao.getMethods();
		for (Method method : ms) {
			AbstractImpl isql = initDaoMethodSet(method);
			isql.methodName = method.getName();
			if (isql == null) {
				throw new BeanExpressionException("dao class[" + dao.getName()
						+ "] Method:[" + method.getName() + "]not impl");
			} else {
				sqlCache.put(method, isql);
			}
		}
		if (conn != null)
			conn.close();
	}

	public void setFieldMatchReg(String fieldMatchReg) {
		this.fieldMatchReg = fieldMatchReg;
	}

	public void setMerge(String merge) {
		this.merge = merge;
	}

	public void setUpdate(String update) {
		this.update = update;
	}

	public void setSave(String save) {
		this.save = save;
	}

	public void setCheck(boolean check) {
		this.check = check;
	}

	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}

	public Object intercept(Object obj, Method method, Object[] args,
			MethodProxy proxy) throws Throwable {
		AbstractImpl action = sqlCache.get(method);
		Connection conn = dataSource.getConnection();
		try {
			return action.exec(conn, args);
		} catch (Exception ex) {
			ex.getMessage();
			ex.printStackTrace();
			throw ex;
		} finally {
			if (conn != null) {
				conn.close();
			}
		}
	}

	private Enhancer enhancer = new Enhancer();

	@SuppressWarnings("rawtypes")
	private AbstractImpl initDaoMethodSet(final Method method) {
		final Val val = method.getAnnotation(Val.class);
		AbstractImpl isql = null;
		String sqlv = null;
		if (val != null) {
			sqlv = val.value().trim();
			if (sqlv.matches("^(select)[\\s](.*)")) {
				if (val.paging()) {

				} else {
					if (List.class.isAssignableFrom(method.getClass())) {
						isql = new AbstractImpl() {

							@SuppressWarnings("unchecked")
							public Object exec(Connection conn, Object[] args)
									throws Exception {
								final LinkedList list = new LinkedList();
								PreparedStatement stmt = null;
								ResultSet rs = null;
								try {
									stmt = getSqlst(conn, args);
									rs = stmt.executeQuery();
									ResultSetMetaData md = rs.getMetaData();
									int w = md.getColumnCount();
									while (rs.next()) {
										list.add(getEle(w, md, rs));
									}
								} finally {
									close(stmt, rs);
								}
								return list;
							}
						};
					} else {
						isql = new AbstractImpl() {
							public Object exec(Connection conn, Object[] args)
									throws Exception {
								PreparedStatement stmt = null;
								ResultSet rs = null;
								try {
									stmt = getSqlst(conn, args);
									rs = stmt.executeQuery();
									ResultSetMetaData md = rs.getMetaData();
									int w = md.getColumnCount();
									if (rs.next()) {
										return getEle(w, md, rs);
									}
									if (rs.next()) {
										throw new Exception(
												"many rseult,not one!");
									}
								} finally {
									close(stmt, rs);
								}
								return null;
							}
						};
					}
				}
			} else if (sqlv.matches("^(update)[\\s](.*)")
					|| sqlv.matches("^(delete)[\\s](.*)")
					|| sqlv.matches("^(insert)[\\s](.*)")) {
				isql = new SqlExec();
			} else {// SP
				isql = new SpExec();
			}

		} else if (method.getName().equals(update)) {
			isql = new UpdateImpl();
		} else if (method.getName().equals(save)) {
			isql = new SaveImpl();
		} else if (method.getName().equals(merge)) {
			isql = new MergeImpl();
		}
		isql.setSql(sqlv);
		isql.returnType = method.getReturnType();
		return isql;
	}

	public void setDao(Class<?> dao) throws Exception {
		this.dao = dao;
	}

	public Object getObject() throws Exception {
		return enhancer.create();
	}

	public Class<?> getObjectType() {
		return dao;
	}

	public boolean isSingleton() {
		return true;
	}

}

/*
 * Method[] methods = cls.getMethods(); for (int i = 0; i <
 * sql.paramMethod.size(); i++) { String col = "get" +
 * paraName.get(i).toLowerCase(); findmethod: { for (Method method : methods) {
 * if (col.equals(method.getName().toLowerCase())) { stmt.setObject(i + 1,
 * method.invoke( args[0], DBUtil.NullObject)); break findmethod; } } throw new
 * SQLException("not find method:" + col); } }
 */
// public void anysql(Sql asql, Connection conn) throws Exception {
// String sql = asql.value();
// int idx = sql.indexOf('#');
// if (idx > 0) {
// StringBuffer sb = new StringBuffer();
// int leng = sql.length();
// sb.append(sql.substring(0, idx));
// String nm = "";
// boolean isFind = true;
// for (int i = idx + 1; i < leng; i++) {
// char c = sql.charAt(i);
// if (c == '#') {
// if (nm.isEmpty()) {
// isFind = true;
// } else {
// isFind = false;
// paraName.addLast(nm);
// sb.append('?');
// nm = "";
// }
// } else {
// if (isFind) {
// nm += c;
// } else {
// sb.append(c);
// }
// }
// }
// sqlstr = sb.toString();
// } else {
// sqlstr = sql;
// }
// }
// private Class getType(Method method) throws Exception {
// String genType = method.toGenericString();
// // int idx = genType.indexOf('(');
// // genType = genType.substring(0, idx);
// Class type;
// int ixr = genType.indexOf('<');
// if (ixr > 0) {
// genType = genType.substring(ixr + 1, genType.indexOf('>'));
// type = ClassUtils.forName(genType,
// ClassUtils.getDefaultClassLoader());
// } else {
// type = HashMap.class;
// }
// return type;
// }